package cn.foolishbird.basic.lock;

import cn.hutool.core.util.StrUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 业务部分重入锁管理器
 *
 * @author Eric
 * @version 1.0
 * @date 2019-12-20
 */
public class ReentryLockManager implements LockManager {

    /**
     * 锁
     */
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 读锁
     */
    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();

    /**
     * 写锁
     */
    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    /**
     * 重入锁池
     */
    private Map<String, Lock> lockPool = new HashMap<>();

    @Override
    public Lock getLock(String lockKey) {
        Lock l = null;
        if (StrUtil.isBlank(lockKey)) {
            return l;
        }
        readLock.lock();
        try {
            l = lockPool.get(lockKey);
        } finally {
            readLock.unlock();
        }
        if (l == null) {
            this.writeLock.lock();
            try {
                if (l == null) {
                    l = new ReentrantLock();
                    lockPool.put(lockKey, l);
                }
            } finally {
                this.writeLock.unlock();
            }
        }
        return l;
    }

    @Override
    public boolean cleanLock(String lockKey) {
        this.writeLock.lock();
        try {
            lockPool.remove(lockKey);
        } finally {
            this.writeLock.unlock();
        }
        return true;
    }

    @Override
    public void lockExecute(String lockKey, LockExecutor executor) throws Exception {
        lockExecute(lockKey, executor, false);
    }

    @Override
    public void lockExecute(String lockKey, LockExecutor executor, boolean clean) throws Exception {
        Lock l = getLock(lockKey);
        if (l == null) {
            throw new Exception("锁管理器，未获取到锁对象");
        }
        l.lock();
        try {
            executor.execute();
        } finally {
            l.unlock();
            if (clean) {
                cleanLock(lockKey);
            }
        }
    }


}
